#include "OclLapack.h"

#include <stdio.h>

#include "../clUtils.h"
#include <vector>
#include <fstream>
#include <clAmdBlas.h>
#include <string.h>

using namespace std;

cl_device_id pickDevice(cl_device_type type) {
    cl_uint numPlatforms;
    CL_CHECK(clGetPlatformIDs(0, NULL, &numPlatforms));
    ASSERT(numPlatforms != 0, "No OpenCL platform found");

    vector<cl_platform_id> platformIds(numPlatforms);
    CL_CHECK(clGetPlatformIDs(numPlatforms, &platformIds[0], NULL));

    for (cl_int i = 0; i < numPlatforms; i++) {
        cl_uint numDevices;
		cl_int code = clGetDeviceIDs(platformIds[i], type, 0, NULL, &numDevices);
        if (numDevices == 0 || code == CL_DEVICE_NOT_FOUND) continue;
		CL_CHECK(code);

        vector<cl_device_id> devices(numDevices);
        CL_CHECK(clGetDeviceIDs(platformIds[i], type, numDevices, &devices[0], NULL));

        // TODO choose device or query less devices
        return devices[0];
    }
    SHOULDNT_REACH();    
}

OclLapack::OclLapack(cl_device_type deviceType) {
	deviceId = pickDevice(deviceType);
	init(deviceId);
}

OclLapack::OclLapack(cl_device_id _deviceId) {
	deviceId = _deviceId;
	init(deviceId);
}

void OclLapack::init(cl_device_id deviceId) {

	cl_int errorCode;
    context = clCreateContext(0, 1, &deviceId, NULL, NULL, &errorCode);
    CL_CHECK(errorCode);

    queue = clCreateCommandQueue(context, deviceId, CL_QUEUE_PROFILING_ENABLE | CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &errorCode);
    CL_CHECK(errorCode);

	CL_CHECK(clAmdBlasSetup());

	programScale = buildProgram(context, deviceId, programScaleSrc);
	
	scale1 = clCreateKernel(programScale, "scale1", &errorCode);
	CL_CHECK(errorCode);

	scaleN = clCreateKernel(programScale, "scaleN", &errorCode);
	CL_CHECK(errorCode);

	programEigenValues = buildProgramFromFile(context, deviceId, "eigenvalue.cl");
	recalculateEigenInterval = clCreateKernel(programEigenValues, "recalculateEigenInterval", &errorCode);
	CL_CHECK(errorCode);
	
	calculateNumEigensInInterval = clCreateKernel(programEigenValues, "calculateNumEigensInInterval", &errorCode);
	CL_CHECK(errorCode);
	recalculateInterval = clCreateKernel(programEigenValues, "recalculateInterval", &errorCode);
	CL_CHECK(errorCode);
}

OclLapack::~OclLapack() {
	clAmdBlasTeardown();
	clReleaseCommandQueue(queue);
    clReleaseContext(context);
}
